// 发布订阅（Publish/Subscriber）模式使用了一个 主题/事件通道对象，管理  主题和事件
// Publish：发布者，发布通知 （通知 主题/事件通道对象  这个主题变化了，执行对应的事件)
// Subscriber：订阅者，注册主题，和事件（ 告诉 主题/事件通道对象，这个主题变换，对应执行事件)
// ╭─────────────╮                 ╭───────────────╮   Fire Event   ╭──────────────╮
// │             │  Publish Event  │               │───────────────>│              │
// │  Publisher  │────────────────>│ Event Channel │                │  Subscriber  │
// │             │                 │               │<───────────────│              │
// ╰─────────────╯                 ╰───────────────╯    Subscribe   ╰──────────────╯
class SubPub {
    constructor () {
        this.observers = {}
    }

    subscriber (topic, callback) {
        if (!this.observers[topic]) {
            this.observers[topic] = []
        }
        this.observers[topic].push(callback)
    }

    publish (topic, ...args) {
        let callbacks = this.observers[topic] || []
        callbacks.forEach(cb => cb.call(this, ...args))
    }
}

// Test
const subject = new SubPub() // 中介
subject.subscriber('两室一厅', () => console.log('张三')) // 张三想买一个两室一厅的房子
subject.subscriber('三室一厅', () => console.log('李四')) // 李四想买一个三室一厅的房子
subject.subscriber('两室一厅', () => console.log('王五')) // 王五想买一个两室一厅的房子
subject.publish('两室一厅') // 当出现一个两室一厅的房子时，中介会通知张三和王五来看房

// 应用
// EventEmitter
class EventEmitter {
    constructor () {
        this.events = {}
    }

    // 实现订阅
    on (type, callback) {
        if (!this.events[type]) {
            this.events[type] = [callback]
        } else {
            this.events[type].push(callback)
        }
    }

    // 删除订阅
    off (type, callback) {
        if (!this.events[type]) return
        this.events[type] = this.events[type].filter(item => {
            return item !== callback
        })
    }

    // 只执行一次订阅事件
    once (type, callback) {
        function fn () {
            callback()
            this.off(type, fn)
        }

        this.on(type, fn)
    }

    // 触发事件
    emit (type, ...args) {
        this.events[type] && this.events[type].forEach(fn => fn.apply(this, args))
    }
}

// Test
const event = new EventEmitter()
const handle = (...args) => {
    console.log(args)
}
event.on('click', handle)
event.emit('click', 1, 2, 3)    // [ 1, 2, 3 ]
event.emit('click', 1, 2, 3, 4) // [ 1, 2, 3, 4 ]
event.off('click', handle)
event.emit('click', 1, 2) // 没有输出
event.once('onceClick', () => {
    console.log('123')
})
event.emit('onceClick') // 123
event.emit('onceClick') // 没有输出
